package com.danieltoms.gcSync.google;

import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;

import net.rim.device.api.xml.parsers.DocumentBuilder;
import net.rim.device.api.xml.parsers.DocumentBuilderFactory;
import net.rim.device.api.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

import com.danieltoms.gcSync.GcSyncConstants;
import com.danieltoms.xml.XMLDocumentWriter;

/**
 * 
 */
public class GoogleContact
{
	private static final String GD_PHONE_NUMBER = "gd:phoneNumber";
	private static final String ATTR_PROTOCOL = "protocol";
	private static final String GD_IM = "gd:im";
	private static final String ATTR_ADDRESS = "address";
	private static final String ATTR_PRIMARY = "primary";
	private static final String ATTR_HREF = "href";
	private static final String ATTR_REL = "rel";
	private static final String ATOM_LINK = "link";
	private static final String GD_ORG_TITLE = "gd:orgTitle";
	private static final String GD_ORG_NAME = "gd:orgName";
	private static final String GD_ORGANIZATION = "gd:organization";
	private static final String ATOM_UPDATED = "updated";
	private static final String ATOM_CONTENT = "content";
	private static final String GD_DELETED = "gd:deleted";
	private static final String ATOM_ENTRY = "entry";
	private static final String XMLNS_GOOGLE_COM_CONTACT_2008_CONTACT = "http://schemas.google.com/contact/2008#contact";
	private static final String XMLNS_GOOGLE_COM_G_2005_KIND = "http://schemas.google.com/g/2005#kind";
	private static final String ATTR_TERM = "term";
	private static final String ATTR_SCHEME = "scheme";
	private static final String ATOM_CATEGORY = "category";
	private static final String XMLNS_GOOGLE_COM_G_2005 = "http://schemas.google.com/g/2005";
	private static final String XMLNS_CONTACT_2008 = "http://schemas.google.com/contact/2008";
	private static final String XMLNS_ATOM = "http://www.w3.org/2005/Atom";
	private static final String XMLNS_GD = "xmlns:gd";
	private static final String XMLNS_G_CONTACT = "xmlns:gContact";
	private static final String XMLNS = "xmlns";
	private static final String GD_POSTAL_ADDRESS = "gd:postalAddress";
	private static final String GOOGLE_TITLE = "title";
	private static final String GD_EMAIL = "gd:email";
	private static final String GOOGLE_ID = "id";
	
	
	Node _contactEntry = null;

	// corresponding to the PIM Contact contant fields we want to populate.
	private String NAME_GIVEN = "";


	private String _notes = null;
	private String _id = null;
	private String _jobTitle = null;
	private String _company = null;
	private String _editURL = null;
	private String _updated = null;

	private boolean _deleted = false;
	private Document _document = null;
	private String _primaryEmail = null;
	private Vector _emails = null;
	private String _primaryPhone = null;
	private Vector _phoneNumbers = null;
	private Vector _postalAddresses = null;
	private Vector _ims = null;

	// google doesn't mark these as primary, we just assume the first one is it.
	// well, they mark "an" address as primary, but it could be other.
	private String _primaryHomeAddress = null;
	private String _primaryWorkAddress = null;

	
	static DocumentBuilderFactory _factory = DocumentBuilderFactory.newInstance();
	static DocumentBuilder _builder = null;

	static
	{

		try
		{
			_builder = _factory.newDocumentBuilder();
		}
		catch (ParserConfigurationException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public Vector getAddresses()
	{
		return _postalAddresses;
	}

	public Vector getIms()
	{
		return _ims;
	}

	public String getPrimaryPhone()
	{
		return _primaryPhone;
	}

	public Vector getPhones()
	{
		return _phoneNumbers;
	}

	public String getPrimaryHomeAddress()
	{
		return _primaryHomeAddress;
	}

	public String getPrimaryWorkAddress()
	{
		return _primaryWorkAddress;
	}

	public GoogleContact()
	{

		Document document = _builder.newDocument();
		Element entry = document.createElement(ATOM_ENTRY);
		entry.setAttribute(XMLNS, XMLNS_ATOM);
		entry.setAttribute(XMLNS_G_CONTACT, XMLNS_CONTACT_2008);
		entry.setAttribute(XMLNS_GD, XMLNS_GOOGLE_COM_G_2005);

		Element category = document.createElement(ATOM_CATEGORY);
		category.setAttribute(ATTR_SCHEME, XMLNS_GOOGLE_COM_G_2005_KIND);
		category.setAttribute(ATTR_TERM, XMLNS_GOOGLE_COM_CONTACT_2008_CONTACT);
		entry.appendChild(category);
		_document = document;
		_contactEntry = entry;

	}

	public GoogleContact(Node node)
	{
		_contactEntry = node;

		parseNode();

	}

	// for update
	public GoogleContact(Node node, Document document)
	{
		_contactEntry = node;
		_document = document;
		parseNode();

	}

	public String getId()
	{
		return _id;
	}

	public String getUpdated()
	{
		return _updated;
	}

	public String getName()
	{
		return NAME_GIVEN;
	}

	public void setNameGiven(String name)
	{
		Node n = getChildWithName(_contactEntry, GOOGLE_TITLE);

		if (n == null)
		{
			n = _document.createElement(GOOGLE_TITLE);
			_contactEntry.appendChild(n);
		}

		if (n.getFirstChild() == null)
		{
			Node text = _document.createTextNode(name);
			n.appendChild(text);
		}
		else
			n.getFirstChild().setNodeValue(name);

	}

	public String getNotes()
	{
		return _notes;
	}

	public String getPrimaryEmail()
	{
		return _primaryEmail;
	}

	void parseNode()
	{
		log("enter parse");
		Node n = null;

		n = getChildWithName(_contactEntry, GOOGLE_ID);
		_id = n.getFirstChild().getNodeValue();
		_id = _id.substring(_id.lastIndexOf('/') + 1, _id.length());

		n = getChildWithName(_contactEntry, GD_DELETED);

		if (n != null)
		{
			// node is deleted
			_deleted = true;
			log("node is deleted");
		}
		else
		{
			n = getChildWithName(_contactEntry, GOOGLE_TITLE);
			if (n != null)
			{
				if (n.getFirstChild() != null)
					NAME_GIVEN = n.getFirstChild().getNodeValue();
				else
					log("nameless contact");

				log("parsing: " + this.NAME_GIVEN);
			}

			// parse google id

			log("parsing: " + this.NAME_GIVEN);
			log("google id:" + _id);

			parseEmails();
			parsePhoneNumbers();
			parseAddresses();
			parseIms();
			parseLinks();

			n = getChildWithName(_contactEntry, ATOM_CONTENT);

			if (n != null && n.getFirstChild() != null)
			{
				_notes = n.getFirstChild().getNodeValue();
			}

			n = getChildWithName(_contactEntry, ATOM_UPDATED);

			_updated = n.getFirstChild().getNodeValue();

			n = getChildWithName(_contactEntry, GD_ORGANIZATION);

			if (n != null)
			{
				Node nn = getChildWithName(n, GD_ORG_NAME);

				if (nn != null && nn.getFirstChild() != null)
				{
					_company = nn.getFirstChild().getNodeValue();
					log("company: " + _company);
				}

				nn = getChildWithName(n, GD_ORG_TITLE);

				if (nn != null && nn.getFirstChild() != null)
				{
					_jobTitle = nn.getFirstChild().getNodeValue();
					log("jobtitle: " + _jobTitle);
				}
			}
		}

	}

	private void parseLinks()
	{
		Vector v = this.getChildrenByName(_contactEntry, ATOM_LINK);

		Enumeration e = v.elements();

		while (e.hasMoreElements())
		{
			Node n = (Node) e.nextElement();

			String s = this.getAttributeValueByName(n, ATTR_REL);

			if (s.equals("edit"))
			{
				_editURL = this.getAttributeValueByName(n, ATTR_HREF);
				break;
			}

		}
	}

	public boolean hasEmail(String email)
	{
		// do a linear searhch :(
		return true;
	}

	private void log(String msg)
	{
		// System.out.println(msg);
	}

	private void parseEmails()
	{
		log("parseEmails start");
		Node n;
		Vector v = getChildrenByName(_contactEntry, GD_EMAIL);

		if (v != null)
		{

			Enumeration e = v.elements();

			if (e != null)
			{
				log("email count in vector: " + v.size());
				while (e.hasMoreElements())
				{
					n = (Node) e.nextElement();

					String primary = getAttributeValueByName(n, ATTR_PRIMARY);
					String email = this.getAttributeValueByName(n, ATTR_ADDRESS);

					if (_emails == null)
						_emails = new Vector();

					String type = null;

					if (primary != null && primary.equalsIgnoreCase(Boolean.TRUE.toString()))
					{
						_primaryEmail = email;
					}
					else
					{
						String rel = getAttributeValueByName(n, ATTR_REL);

						if (rel.indexOf(GcSyncConstants.HOME_TYPE) != -1)
						{
							type = GcSyncConstants.HOME_TYPE;
						}
						else if (rel.indexOf(GcSyncConstants.WORK_TYPE) != -1)
						{
							type = GcSyncConstants.WORK_TYPE;
						}
						else if (rel.indexOf(GcSyncConstants.OTHER_TYPE) != -1)
						{
							type = GcSyncConstants.OTHER_TYPE;
						}
					}

					GoogleTypedField gEmail = new GoogleTypedField(email, type);

					_emails.addElement(gEmail);

				}
			}
		}

		log("parseEmails end");
	}


	private void parsePhoneNumbers()
	{
		log("parsePhoneNumbers start");
		Node n;
		Vector v = getChildrenByName(_contactEntry, GD_PHONE_NUMBER);

		if (v != null)
		{

			Enumeration e = v.elements();

			log("phone count in vector: " + v.size());
			while (e.hasMoreElements())
			{
				n = (Node) e.nextElement();

				String primary = getAttributeValueByName(n, ATTR_PRIMARY);
				String phone = n.getFirstChild().getNodeValue();

				if (primary != null && primary.equalsIgnoreCase(Boolean.TRUE.toString()))
				{
					_primaryPhone = phone;
				}
				else
				{
					String rel = getAttributeValueByName(n, ATTR_REL);
					String type = null;

					if (rel.endsWith(GcSyncConstants.HOME_TYPE))
					{
						type = GcSyncConstants.HOME_TYPE;
					}
					else if (rel.endsWith(GcSyncConstants.WORK_TYPE))
					{
						type = GcSyncConstants.WORK_TYPE;
					}
					else if (rel.endsWith(GcSyncConstants.MOBILE_TYPE))
					{
						type = GcSyncConstants.MOBILE_TYPE;
					}
					else if (rel.endsWith(GcSyncConstants.HOME_FAX_TYPE))
					{
						type = GcSyncConstants.HOME_FAX_TYPE;
					}
					else if (rel.endsWith(GcSyncConstants.WORK_FAX_TYPE))
					{
						type = GcSyncConstants.WORK_FAX_TYPE;
					}
					else if (rel.endsWith(GcSyncConstants.PAGER_TYPE))
					{
						type = GcSyncConstants.PAGER_TYPE;
					}
					GoogleTypedField field = new GoogleTypedField(phone, type);

					if (_phoneNumbers == null)
						_phoneNumbers = new Vector();

					_phoneNumbers.addElement(field);
				}

			}
		}

		log("parsePhoneNumbers end");
	}


	Node getChildWithName(Node n, String nodeName)
	{

		NodeList l = n.getChildNodes();
		Node ret = null;
		if (l != null)
		{
			for (int i = 0; i < l.getLength(); i++)
			{

				Node node = l.item(i);
				if (node.getNodeName() != null)
					if (node.getNodeName().equals(nodeName))
					{
						ret = node;
						break;
					}
			}
		}

		return ret;
	}

	Vector getChildrenByName(Node n, String nodeName)
	{
		String value = "";
		NodeList l = n.getChildNodes();
		Vector list = null;

		for (int i = 0; i < l.getLength(); i++)
		{

			Node node = l.item(i);
			if (node.getNodeName().equals(nodeName))
			{
				if (list == null)
					list = new Vector();

				list.addElement(node);
			}
		}

		return list;
	}

	String getAttributeValueByName(Node n, String attributeName)
	{

		NamedNodeMap map = n.getAttributes();
		String value = "";
		if (map.getLength() > 0)
		{

			Node node = map.getNamedItem(attributeName);

			if (node != null)
			{
				value = node.getNodeValue();

			}
		}

		return value;
	}

	public boolean deleted()
	{
		return _deleted;
	}

	public Date updated()
	{
		return null;
	}

	public String getJobTitle()
	{
		return _jobTitle;
	}

	public String getCompany()
	{
		return _company;
	}

	public void setNotes(String value)
	{
		
		// TODO Auto-generated method stub
		if (value == null)
			value = new String();

		// todo: handle create cas
		Node notes = this.getChildWithName(_contactEntry, ATOM_CONTENT);

		if(notes==null)
		{
			Element noteElement = _document.createElement(ATOM_CONTENT);
			noteElement.appendChild(_document.createTextNode(value));
			_contactEntry.appendChild(noteElement);
		}
		else
			notes.getFirstChild().setNodeValue(value);
	}

	public String getEditURL()
	{
		return _editURL;
	}

	public void printXML(OutputStream os)
	{
		PrintStream ps = new PrintStream(os);
		XMLDocumentWriter writer = new XMLDocumentWriter(ps);

		writer.print(_contactEntry);
	}

	public void prettyPrintXML(OutputStream os)
	{
		PrintStream ps = new PrintStream(os);
		XMLDocumentWriter writer = new XMLDocumentWriter(ps);

		writer.prettyPrint(_contactEntry);
	}

	public Vector getEmails()
	{
		return _emails;
	}

	public void setEmails(Vector emails)
	{
		this._emails = emails;

		// clear existing emails

		Element element;
		Vector v = getChildrenByName(_contactEntry, GD_EMAIL);
		Enumeration e;
		if (v != null)
		{

			e = v.elements();

			if (e != null)
			{
				while (e.hasMoreElements())
				{
					element = (Element) e.nextElement();
					element.getParentNode().removeChild(element);
				}
			}
		}

		if (emails != null)
		{
			// <gd:phoneNumber rel="home">
			e = emails.elements();
			String rel = XMLNS_GOOGLE_COM_G_2005+"#";
			
			if (e.hasMoreElements())
			{
				GoogleTypedField gtf = (GoogleTypedField) e.nextElement();
				element = _document.createElementNS(XMLNS_GOOGLE_COM_G_2005, GD_EMAIL);
				element.setAttribute(ATTR_REL, rel + GcSyncConstants.OTHER_TYPE);
				element.setAttribute(ATTR_PRIMARY, "true");
				element.setAttribute(ATTR_ADDRESS, gtf.getValue());
				_contactEntry.appendChild(element);
			}

			while (e.hasMoreElements())
			{
				GoogleTypedField gtf = (GoogleTypedField) e.nextElement();
				element = _document.createElementNS(XMLNS_GOOGLE_COM_G_2005, GD_EMAIL);
				element.setAttribute(ATTR_REL, rel + gtf.getType());
				element.setAttribute(ATTR_ADDRESS, gtf.getValue());
				_contactEntry.appendChild(element);
			}
		}

	}
	
	private void parseAddresses()
	{
		log("parseAddress start");
		Node n;
		Vector v = getChildrenByName(_contactEntry, GD_POSTAL_ADDRESS);

		if (v != null)
		{

			Enumeration e = v.elements();

			log("address  count in vector: " + v.size());
			while (e.hasMoreElements())
			{
				n = (Node) e.nextElement();

				String address = n.getFirstChild().getNodeValue();

				String rel = getAttributeValueByName(n, ATTR_REL);
				String type = null;

				if (rel.indexOf(GcSyncConstants.HOME_TYPE) != -1)
				{
					type = GcSyncConstants.HOME_TYPE;

					if (_primaryHomeAddress == null)
					{
						_primaryHomeAddress = address;
						continue;
					}
				}
				else if (rel.indexOf(GcSyncConstants.WORK_TYPE) != -1)
				{
					type = GcSyncConstants.WORK_TYPE;

					if (_primaryWorkAddress == null)
					{
						_primaryWorkAddress = address;
						continue;
					}
				}
				else if (rel.indexOf(GcSyncConstants.OTHER_TYPE) != -1)
				{
					type = GcSyncConstants.OTHER_TYPE;
				}

				GoogleTypedField field = new GoogleTypedField(address, type);

				if (_postalAddresses == null)
					_postalAddresses = new Vector();

				_postalAddresses.addElement(field);
			}
		}

		log("parseAddress end");
	}
	
	public void setAddresses(Vector addresses)
	{
		this._emails = addresses;

		// clear existing addresses

		Element element;
		Vector v = getChildrenByName(_contactEntry, GD_POSTAL_ADDRESS);
		Enumeration e;
		if (v != null)
		{

			e = v.elements();

			if (e != null)
			{
				while (e.hasMoreElements())
				{
					element = (Element) e.nextElement();
					element.getParentNode().removeChild(element);
				}
			}
		}

		if (addresses != null)
		{
			// <gd:phoneNumber rel="home">
			e = addresses.elements();
			String rel = XMLNS_GOOGLE_COM_G_2005+"#";
			
			while (e.hasMoreElements())
			{
				GoogleTypedField gtf = (GoogleTypedField) e.nextElement();
				element = _document.createElementNS(XMLNS_GOOGLE_COM_G_2005, GD_POSTAL_ADDRESS);
				element.setAttribute(ATTR_REL, rel + gtf.getType());
				Text value = _document.createTextNode(gtf.getValue());
				element.appendChild(value);
				_contactEntry.appendChild(element);
			}
		}

	}	
	
	public void setPhoneNumbers(Vector phoneNumbers)
	{
		_phoneNumbers = phoneNumbers;

		// clear existing addresses

		Element element;
		Vector v = getChildrenByName(_contactEntry, GD_PHONE_NUMBER);
		Enumeration e;
		if (v != null)
		{

			e = v.elements();

			if (e != null)
			{
				while (e.hasMoreElements())
				{
					element = (Element) e.nextElement();
					element.getParentNode().removeChild(element);
				}
			}
		}

		if (phoneNumbers != null)
		{
			// <gd:phoneNumber rel="home">
			e = phoneNumbers.elements();
			String rel = XMLNS_GOOGLE_COM_G_2005+"#";
			
			while (e.hasMoreElements())
			{
				GoogleTypedField gtf = (GoogleTypedField) e.nextElement();
				element = _document.createElementNS(XMLNS_GOOGLE_COM_G_2005, GD_PHONE_NUMBER);
				element.setAttribute(ATTR_REL, rel + gtf.getType());
				Text value = _document.createTextNode(gtf.getValue());
				element.appendChild(value);
				_contactEntry.appendChild(element);
			}
		}

	}
	
	// <gd:im address="google im2"
	// protocol="http://schemas.google.com/g/2005#GOOGLE_TALK"
	// rel="http://schemas.google.com/g/2005#other"/>

	private void parseIms()
	{
		log("parseIms start");
		Node n;
		Vector v = getChildrenByName(_contactEntry, GD_IM);

		if (v != null)
		{

			Enumeration e = v.elements();

			if (e != null)
			{
				log("im count in vector: " + v.size());

				while (e.hasMoreElements())
				{
					n = (Node) e.nextElement();

					String email = this.getAttributeValueByName(n, ATTR_ADDRESS);
					String protocol = this.getAttributeValueByName(n, ATTR_PROTOCOL);

					String type = null;

					if (protocol.endsWith(GcSyncConstants.IM_GOOGLE_TALK))
					{
						type = GcSyncConstants.IM_GOOGLE_TALK;
					}
					else if (protocol.endsWith(GcSyncConstants.IM_AIM))
					{
						type = GcSyncConstants.IM_AIM;
					}
					else if (protocol.endsWith(GcSyncConstants.IM_ICQ))
					{
						type = GcSyncConstants.IM_ICQ;
					}
					else if (protocol.endsWith(GcSyncConstants.IM_JABBER))
					{
						type = GcSyncConstants.IM_JABBER;
					}
					else if (protocol.endsWith(GcSyncConstants.IM_MSN))
					{
						type = GcSyncConstants.IM_MSN;
					}
					else if (protocol.endsWith(GcSyncConstants.IM_YAHOO))
					{
						type = GcSyncConstants.IM_YAHOO;
					}
					GoogleTypedField gEmail = new GoogleTypedField(email, type);

					if (_ims == null)
						_ims = new Vector();

					_ims.addElement(gEmail);
				}

			}
		}

		log("parseIms end");
	}

	/*
	 * <gd:im address="google im1" protocol="http://schemas.google.com/g/2005#GOOGLE_TALK" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="google im2" protocol="http://schemas.google.com/g/2005#GOOGLE_TALK" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="google im3" protocol="http://schemas.google.com/g/2005#GOOGLE_TALK" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="aim im1" protocol="http://schemas.google.com/g/2005#AIM" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="aim im2" protocol="http://schemas.google.com/g/2005#AIM" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="aim im3" protocol="http://schemas.google.com/g/2005#AIM" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="yahoo im1" protocol="http://schemas.google.com/g/2005#YAHOO" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="yahoo im2" protocol="http://schemas.google.com/g/2005#YAHOO" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="yahoo im3" protocol="http://schemas.google.com/g/2005#YAHOO" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="msn im1" protocol="http://schemas.google.com/g/2005#MSN" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="msn im2" protocol="http://schemas.google.com/g/2005#MSN" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="msn im3" protocol="http://schemas.google.com/g/2005#MSN" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="icq im1" protocol="http://schemas.google.com/g/2005#ICQ" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="icq im2" protocol="http://schemas.google.com/g/2005#ICQ" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="icq im3" protocol="http://schemas.google.com/g/2005#ICQ" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="jabber im1" protocol="http://schemas.google.com/g/2005#JABBER" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="jabber im2" protocol="http://schemas.google.com/g/2005#JABBER" rel="http://schemas.google.com/g/2005#other"/>
<gd:im address="jabber im3" protocol="http://schemas.google.com/g/2005#JABBER" rel="http://schemas.google.com/g/2005#other"/>
	 */
	public void setInstantMessages(Vector ims)
	{
		this._ims = ims;

		// clear existing addresses

		Element element;
		Vector v = getChildrenByName(_contactEntry, GD_IM);
		Enumeration e;
		if (v != null)
		{

			e = v.elements();

			if (e != null)
			{
				while (e.hasMoreElements())
				{
					element = (Element) e.nextElement();
					element.getParentNode().removeChild(element);
				}
			}
		}

		if (ims != null)
		{
			// <gd:phoneNumber rel="home">
			e = ims.elements();
			String rel = XMLNS_GOOGLE_COM_G_2005+"#"+GcSyncConstants.OTHER_TYPE;
			
			String protocol = XMLNS_GOOGLE_COM_G_2005+"#";
			while (e.hasMoreElements())
			{
				GoogleTypedField gtf = (GoogleTypedField) e.nextElement();
				element = _document.createElementNS(XMLNS_GOOGLE_COM_G_2005, GD_IM);
				element.setAttribute(ATTR_PROTOCOL, protocol + gtf.getType());
				element.setAttribute(ATTR_REL, rel);
				element.setAttribute(ATTR_ADDRESS, gtf.getValue());
				_contactEntry.appendChild(element);
			}
		}

	}		
}
